package com.gitee.ed.example.caffeine;

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.Policy;
import com.github.benmanes.caffeine.cache.RemovalListener;
import com.github.benmanes.caffeine.cache.stats.CacheStats;
import org.checkerframework.checker.index.qual.NonNegative;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.checker.nullness.qual.PolyNull;

import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Function;

/**
 * 以字符串为唯一键的缓存实例
 *
 * @author hongda.li
 */
public class StringCache<V> implements Cache<String, V> {

    private Cache<String, V> cache;

    private final Caffeine<Object, Object> caffeine;

    public StringCache() {
        this.caffeine = Caffeine.newBuilder();
    }

    public StringCache<V> configure(Consumer<Caffeine<Object, Object>> consumer) {
        consumer.accept(this.caffeine);
        return this;
    }

    public StringCache<V> init(int initialCapacity) {
        this.caffeine.initialCapacity(initialCapacity);
        return this;
    }

    public StringCache<V> maxSize(int maxSize) {
        this.caffeine.maximumSize(maxSize);
        return this;
    }

    /**
     * 指定过期时间，在写入后的指定时间后过期
     *
     * @param expireAfterWrite 写入后的过期时间
     * @param timeUnit         时间单位
     * @return 链式调用
     */
    public StringCache<V> expireAfterWrite(long expireAfterWrite, TimeUnit timeUnit) {
        this.caffeine.expireAfterWrite(expireAfterWrite, timeUnit);
        return this;
    }

    /**
     * 指定过期时间，在访问后的指定时间后过期
     *
     * @param expireAfterAccess 访问后的过期时间
     * @param timeUnit          时间单位
     * @return 链式调用
     */
    public StringCache<V> expireAfterAccess(long expireAfterAccess, TimeUnit timeUnit) {
        this.caffeine.expireAfterAccess(expireAfterAccess, timeUnit);
        return this;
    }

    /**
     * 配置 slf4j 的日志监听器
     *
     * @return 链式调用
     */
    public StringCache<V> slf4j() {
        return this.removeListener(new LoggerListener<>());
    }

    /**
     * 缓存被移除时的监听策略
     *
     * @param listener 缓存移除监听器
     * @return 链式调用
     */
    public StringCache<V> removeListener(RemovalListener<String, V> listener) {
        this.caffeine.evictionListener(listener);
        return this;
    }

    /**
     * 获取缓存实例
     * 如果缓存尚未构造，则构造缓存实例
     *
     * @return 缓存实例
     */
    public Cache<String, V> getCache() {
        if (this.cache == null) {
            synchronized (this) {
                this.cache = this.caffeine.build();
            }
        }
        return cache;
    }

    @Override
    public @Nullable V getIfPresent(String key) {
        return getCache().getIfPresent(key);
    }

    @Override
    public @PolyNull V get(String key, Function<? super String, ? extends @PolyNull V> mappingFunction) {
        return getCache().get(key, mappingFunction);
    }

    @Override
    public Map<String, V> getAllPresent(Iterable<? extends String> keys) {
        return getCache().getAllPresent(keys);
    }

    @Override
    public Map<String, V> getAll(Iterable<? extends String> keys,
                                 Function<? super Set<? extends String>, ? extends Map<? extends String, ? extends V>> function) {
        return getCache().getAll(keys, function);
    }

    @Override
    public void put(String key, V value) {
        getCache().put(key, value);
    }

    @Override
    public void putAll(Map<? extends String, ? extends V> map) {
        getCache().putAll(map);
    }

    @Override
    public void invalidate(String key) {
        getCache().invalidate(key);
    }

    @Override
    public void invalidateAll(Iterable<? extends String> keys) {
        getCache().invalidateAll(keys);
    }

    @Override
    public void invalidateAll() {
        getCache().invalidateAll();
    }

    @Override
    public @NonNegative long estimatedSize() {
        return getCache().estimatedSize();
    }

    @Override
    public CacheStats stats() {
        return getCache().stats();
    }

    @Override
    public ConcurrentMap<String, V> asMap() {
        return getCache().asMap();
    }

    @Override
    public void cleanUp() {
        getCache().cleanUp();
    }

    @Override
    public Policy<String, V> policy() {
        return getCache().policy();
    }
}
